import 'package:math_keyboard/src/foundation/node.dart';
import 'package:math_keyboard/src/foundation/extended_nodes.dart';

/// Types of validation errors.
enum ValidationErrorType {
  emptyMatrix,
  invalidMatrixDimensions,
  incompleteLimitExpression,
  missingSubscriptSuperscript,
  emptyFunction,
  invalidStructure,
  circularReference,
}

/// Represents a validation error.
class ValidationError {
  /// Creates a [ValidationError].
  const ValidationError({
    required this.type,
    required this.message,
    this.node,
    this.suggestion,
  });

  /// The type of validation error.
  final ValidationErrorType type;

  /// Human-readable error message.
  final String message;

  /// The node where the error occurred (if applicable).
  final TeXNode? node;

  /// Suggested fix for the error (if available).
  final String? suggestion;

  @override
  String toString() => 'ValidationError: $message';
}

/// Validator for mathematical structure integrity.
class StructureValidator {
  /// Validates a matrix structure.
  static List<ValidationError> validateMatrix(TeXMatrix matrix) {
    final errors = <ValidationError>[];

    // Check if matrix has valid dimensions
    if (matrix.rows <= 0 || matrix.columns <= 0) {
      errors.add(ValidationError(
        type: ValidationErrorType.invalidMatrixDimensions,
        message: 'Matrix must have positive dimensions',
        suggestion: 'Ensure rows and columns are greater than 0',
      ));
    }

    // Check if all cells exist
    for (int row = 0; row < matrix.rows; row++) {
      for (int col = 0; col < matrix.columns; col++) {
        try {
          final cell = matrix.getCell(row, col);
          if (cell.parent != matrix) {
            errors.add(ValidationError(
              type: ValidationErrorType.invalidStructure,
              message: 'Matrix cell at ($row, $col) has incorrect parent reference',
              node: cell,
              suggestion: 'Fix parent-child relationships in matrix structure',
            ));
          }
        } catch (e) {
          errors.add(ValidationError(
            type: ValidationErrorType.invalidMatrixDimensions,
            message: 'Cannot access matrix cell at ($row, $col): $e',
            suggestion: 'Check matrix dimensions and cell allocation',
          ));
        }
      }
    }

    // Check for completely empty matrix
    bool hasContent = false;
    for (int row = 0; row < matrix.rows && !hasContent; row++) {
      for (int col = 0; col < matrix.columns && !hasContent; col++) {
        final cell = matrix.getCell(row, col);
        if (cell.children.isNotEmpty) {
          hasContent = true;
        }
      }
    }

    if (!hasContent) {
      errors.add(ValidationError(
        type: ValidationErrorType.emptyMatrix,
        message: 'Matrix is completely empty',
        suggestion: 'Add content to at least one matrix cell',
      ));
    }

    return errors;
  }

  /// Validates a limit expression structure.
  static List<ValidationError> validateLimitExpression(TeXLimitExpression expr) {
    final errors = <ValidationError>[];

    // Check required components based on expression type
    switch (expr.expression) {
      case r'\int':
        if (!expr.hasLowerLimit || !expr.hasUpperLimit || !expr.hasBody) {
          errors.add(ValidationError(
            type: ValidationErrorType.incompleteLimitExpression,
            message: 'Integral must have lower limit, upper limit, and integrand',
            suggestion: 'Ensure all three components are present',
          ));
        }
        break;
      case r'\sum':
      case r'\prod':
        if (!expr.hasLowerLimit || !expr.hasUpperLimit) {
          errors.add(ValidationError(
            type: ValidationErrorType.incompleteLimitExpression,
            message: '${expr.expression} must have both lower and upper limits',
            suggestion: 'Add missing limit expressions',
          ));
        }
        break;
      case r'\lim':
        if (!expr.hasLowerLimit) {
          errors.add(ValidationError(
            type: ValidationErrorType.incompleteLimitExpression,
            message: 'Limit must have an approach expression',
            suggestion: 'Add the variable and approach value (e.g., x → 0)',
          ));
        }
        break;
    }

    // Check for empty limits
    if (expr.hasLowerLimit && expr.lowerLimit != null) {
      if (expr.lowerLimit!.children.isEmpty) {
        errors.add(ValidationError(
          type: ValidationErrorType.incompleteLimitExpression,
          message: 'Lower limit is empty',
          node: expr.lowerLimit,
          suggestion: 'Add content to the lower limit',
        ));
      }
    }

    if (expr.hasUpperLimit && expr.upperLimit != null) {
      if (expr.upperLimit!.children.isEmpty) {
        errors.add(ValidationError(
          type: ValidationErrorType.incompleteLimitExpression,
          message: 'Upper limit is empty',
          node: expr.upperLimit,
          suggestion: 'Add content to the upper limit',
        ));
      }
    }

    if (expr.hasBody && expr.bodyNode != null) {
      if (expr.bodyNode!.children.isEmpty) {
        errors.add(ValidationError(
          type: ValidationErrorType.incompleteLimitExpression,
          message: 'Function body is empty',
          node: expr.bodyNode,
          suggestion: 'Add the function to be integrated/summed',
        ));
      }
    }

    return errors;
  }

  /// Validates a subscript/superscript structure.
  // static List<ValidationError> validateSubscriptSuperscript(TeXSubscriptSuperscript expr) {
  //   final errors = <ValidationError>[];
  //
  //   // Check that at least one of subscript or superscript exists
  //   if (!expr.hasSubscript && !expr.hasSuperscript) {
  //     errors.add(ValidationError(
  //       type: ValidationErrorType.missingSubscriptSuperscript,
  //       message: 'Subscript/superscript structure must have at least one component',
  //       suggestion: 'Add either a subscript or superscript',
  //     ));
  //   }
  //
  //   // Check for empty components
  //   if (expr.hasSubscript && expr.subscriptNode != null) {
  //     if (expr.subscriptNode!.children.isEmpty) {
  //       errors.add(ValidationError(
  //         type: ValidationErrorType.missingSubscriptSuperscript,
  //         message: 'Subscript is empty',
  //         node: expr.subscriptNode,
  //         suggestion: 'Add content to the subscript',
  //       ));
  //     }
  //   }
  //
  //   if (expr.hasSuperscript && expr.superscriptNode != null) {
  //     if (expr.superscriptNode!.children.isEmpty) {
  //       errors.add(ValidationError(
  //         type: ValidationErrorType.missingSubscriptSuperscript,
  //         message: 'Superscript is empty',
  //         node: expr.superscriptNode,
  //         suggestion: 'Add content to the superscript',
  //       ));
  //     }
  //   }
  //
  //   // Check base node
  //   if (expr.baseNode.children.isEmpty) {
  //     errors.add(ValidationError(
  //       type: ValidationErrorType.missingSubscriptSuperscript,
  //       message: 'Base expression is empty',
  //       node: expr.baseNode,
  //       suggestion: 'Add content to the base expression',
  //     ));
  //   }
  //
  //   return errors;
  // }

  /// Validates a general function structure.
  static List<ValidationError> validateFunction(TeXFunction func) {
    final errors = <ValidationError>[];

    // Check that function has the expected number of arguments
    if (func.args.length != func.argNodes.length) {
      errors.add(ValidationError(
        type: ValidationErrorType.invalidStructure,
        message: 'Function argument count mismatch: expected ${func.args.length}, got ${func.argNodes.length}',
        suggestion: 'Fix function argument structure',
      ));
    }

    // Check for empty required arguments
    for (int i = 0; i < func.argNodes.length; i++) {
      final argNode = func.argNodes[i];
      if (argNode.children.isEmpty) {
        errors.add(ValidationError(
          type: ValidationErrorType.emptyFunction,
          message: 'Function argument ${i + 1} is empty',
          node: argNode,
          suggestion: 'Add content to the function argument',
        ));
      }

      // Check parent-child relationship
      if (argNode.parent != func) {
        errors.add(ValidationError(
          type: ValidationErrorType.invalidStructure,
          message: 'Function argument ${i + 1} has incorrect parent reference',
          node: argNode,
          suggestion: 'Fix parent-child relationships',
        ));
      }
    }

    return errors;
  }

  /// Validates the entire document structure.
  static List<ValidationError> validateDocument(TeXNode root) {
    final errors = <ValidationError>[];
    final visitedNodes = <TeXNode>{};

    void validateNodeRecursive(TeXNode node) {
      // Check for circular references
      if (visitedNodes.contains(node)) {
        errors.add(ValidationError(
          type: ValidationErrorType.circularReference,
          message: 'Circular reference detected in document structure',
          node: node,
          suggestion: 'Fix circular parent-child relationships',
        ));
        return;
      }

      visitedNodes.add(node);

      // Validate each child
      for (final child in node.children) {
        if (child is TeXFunction) {
          // Validate specific function types
          if (child is TeXMatrix) {
            errors.addAll(validateMatrix(child));
          } else if (child is TeXLimitExpression) {
            errors.addAll(validateLimitExpression(child));
          // } else if (child is TeXSubscriptSuperscript) {
          //   errors.addAll(validateSubscriptSuperscript(child));
          } else {
            errors.addAll(validateFunction(child));
          }

          // Recursively validate function arguments
          for (final argNode in child.argNodes) {
            validateNodeRecursive(argNode);
          }
        }
      }

      visitedNodes.remove(node);
    }

    validateNodeRecursive(root);
    return errors;
  }

  /// Validates a specific node and its children.
  static List<ValidationError> validateNode(TeXNode node) {
    final errors = <ValidationError>[];

    for (final child in node.children) {
      if (child is TeXMatrix) {
        errors.addAll(validateMatrix(child));
      } else if (child is TeXLimitExpression) {
        errors.addAll(validateLimitExpression(child));
      // } else if (child is TeXSubscriptSuperscript) {
      //   errors.addAll(validateSubscriptSuperscript(child));
      } else if (child is TeXFunction) {
        errors.addAll(validateFunction(child));
      }
    }

    return errors;
  }

  /// Checks if a document is valid (has no errors).
  static bool isValid(TeXNode root) {
    return validateDocument(root).isEmpty;
  }

  /// Gets a summary of validation results.
  static ValidationSummary getValidationSummary(TeXNode root) {
    final errors = validateDocument(root);
    final errorsByType = <ValidationErrorType, int>{};

    for (final error in errors) {
      errorsByType[error.type] = (errorsByType[error.type] ?? 0) + 1;
    }

    return ValidationSummary(
      isValid: errors.isEmpty,
      totalErrors: errors.length,
      errorsByType: errorsByType,
      errors: errors,
    );
  }
}

/// Summary of validation results.
class ValidationSummary {
  /// Creates a [ValidationSummary].
  const ValidationSummary({
    required this.isValid,
    required this.totalErrors,
    required this.errorsByType,
    required this.errors,
  });

  /// Whether the document is valid.
  final bool isValid;

  /// Total number of errors found.
  final int totalErrors;

  /// Count of errors by type.
  final Map<ValidationErrorType, int> errorsByType;

  /// List of all validation errors.
  final List<ValidationError> errors;

  @override
  String toString() {
    if (isValid) {
      return 'ValidationSummary: Document is valid';
    } else {
      return 'ValidationSummary: $totalErrors errors found';
    }
  }
}